package mrpanyu.guitool.example;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.MessageDigest;
import java.util.Base64;

import mrpanyu.guitool.base.annotation.Action;
import mrpanyu.guitool.base.annotation.OnParameterChange;
import mrpanyu.guitool.base.annotation.Parameter;
import mrpanyu.guitool.base.annotation.ToolModel;
import mrpanyu.guitool.base.model.ParameterType;
import mrpanyu.guitool.base.model.Tool;

/**
 * An example tool to calculate digest value, using annotations to mark UI
 * behavior
 * 
 * @author panyu
 *
 */
@ToolModel(displayName = "Digest Tool", description = "Example tool to calculate digest value from text or file.", enableProfiles = true)
public class DigestTool {

	/* Parameters are input values for your tool */

	/*
	 * Example of a parameter which is choose from several options on UI, e.g swing
	 * combobox.
	 */
	@Parameter(displayName = "Source Type", order = 0, description = "Whether to calculate digest from text or file.", type = ParameterType.SELECT, options = {
			"Text", "File" })
	private String sourceType;

	/* Example of a parameter which is a multi-line text input on UI. */
	@Parameter(displayName = "Text", order = 1, description = "Text to do the digest.", type = ParameterType.MULTILINE_TEXT)
	private String text;

	/*
	 * Example of a parameter which is a single-line text input on UI. With a
	 * default value.
	 */
	@Parameter(displayName = "Encoding", order = 2, description = "Text encoding to calculate the digest.", type = ParameterType.TEXT)
	private String encoding = "UTF-8";

	/*
	 * Example of a parameter which is a file select on UI, and is by default
	 * invisible.
	 */
	@Parameter(displayName = "File", order = 3, description = "File to do the digest.", type = ParameterType.FILE, visible = false)
	private File file;

	@Parameter(displayName = "Algorithm", order = 4, description = "Digest algorithm to use.", type = ParameterType.SELECT, options = {
			"MD5", "SHA1", "SHA-256" })
	private String algorithm;

	/*
	 * Parameter change handler methods are invoked when the related parameter value
	 * is changed by user. Annotated method can have none parameters or one
	 * parameter of type Tool.
	 */
	@OnParameterChange("sourceType")
	public void onSourceTypeChange(Tool t) {
		// You can change parameter values directly by changing field values.
		// If you need to change options or visibilities, you need to call
		// t.getParameter() to get the related parameter object.
		if ("Text".equals(sourceType)) {
			t.getParameter("text").setVisible(true);
			t.getParameter("encoding").setVisible(true);
			t.getParameter("file").setVisible(false);
		} else {
			t.getParameter("text").setVisible(false);
			t.getParameter("encoding").setVisible(false);
			t.getParameter("file").setVisible(true);
		}
	}

	/*
	 * Actions are usually shown as buttons on UI, and are called when user clicks
	 * the related button. Annotated method can have none parameters or one
	 * parameter of type Tool.
	 */
	@Action(displayName = "Calc Digest", order = 0, description = "Calcuate the digest and print on output.")
	public void calcDigest(Tool t) throws Exception {
		// Here we do digest according to user input
		String digest = null;
		if ("Text".equals(sourceType)) {
			digest = digestText(text, encoding, algorithm);
		} else if ("File".equals(sourceType)) {
			digest = digestFile(file, algorithm);
		}
		// Show output to user
		t.infoMessage(digest);
	}

	/* Another action to clear old output messages */
	@Action(displayName = "Clear", order = 1, description = "Clear output messages.")
	public void clearMessages(Tool t) {
		t.clearMessages();
	}

	private String digestText(String text, String encoding, String algorithm) throws Exception {
		byte[] textBytes = text.getBytes(encoding);
		MessageDigest md = MessageDigest.getInstance(algorithm);
		byte[] digest = md.digest(textBytes);
		String base64 = Base64.getEncoder().encodeToString(digest);
		return base64;
	}

	private String digestFile(File f, String algorithm) throws Exception {
		if (!f.exists()) {
			throw new FileNotFoundException(f.getAbsolutePath());
		}
		MessageDigest md = MessageDigest.getInstance(algorithm);
		try (FileInputStream in = new FileInputStream(f)) {
			byte[] buffer = new byte[1024];
			int len = in.read(buffer);
			while (len > 0) {
				md.update(buffer, 0, len);
				len = in.read(buffer);
			}
			byte[] digest = md.digest();
			String base64 = Base64.getEncoder().encodeToString(digest);
			return base64;
		}
	}

}
